<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>Zookeeper基础知识 | 无人深空</title><meta name="keywords" content="Zookeeper"><meta name="author" content="zcm"><meta name="copyright" content="zcm"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="Zookeeper 基础知识1.Zookeeper 入门 概述  Zookeeper 是一个开源的分布式的，为分布式框架提供协调服务的 Apache 项目    Zookeeper 工作机制    特点  Zookeeper  一个领导者（Leader），多个跟随者（Follower）组成的集群   集群中只要有半数以上节点存活，Zookeeper 集群就能正常服务  Zookeeper 适合安装">
<meta property="og:type" content="article">
<meta property="og:title" content="Zookeeper基础知识">
<meta property="og:url" content="https://gitee.com/zcmmmm/zcmmmm/2022/11/05/zookeeper%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/index.html">
<meta property="og:site_name" content="无人深空">
<meta property="og:description" content="Zookeeper 基础知识1.Zookeeper 入门 概述  Zookeeper 是一个开源的分布式的，为分布式框架提供协调服务的 Apache 项目    Zookeeper 工作机制    特点  Zookeeper  一个领导者（Leader），多个跟随者（Follower）组成的集群   集群中只要有半数以上节点存活，Zookeeper 集群就能正常服务  Zookeeper 适合安装">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/Zookeeper.png">
<meta property="article:published_time" content="2022-11-05T02:47:38.000Z">
<meta property="article:modified_time" content="2023-05-02T02:12:28.395Z">
<meta property="article:author" content="zcm">
<meta property="article:tag" content="Zookeeper">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/Zookeeper.png"><link rel="shortcut icon" href="/img/favicon.png"><link rel="canonical" href="https://gitee.com/zcmmmm/zcmmmm/2022/11/05/zookeeper%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"/search.xml","preload":false,"languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":200},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#1f1f1f","position":"bottom-left"},
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'Zookeeper基础知识',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2023-05-02 10:12:28'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="/css/categories.css?v1"><link rel="stylesheet" href="/css/navigation.css?v1"><link rel="stylesheet" href="/css/custom.css?v1"><meta name="generator" content="Hexo 6.3.0"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://s2.loli.net/2022/09/22/36LN4kcjYOd9HT8.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">43</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">25</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">11</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="not-top-img" id="page-header"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">无人深空</a></span><div id="menus"></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div></div><div id="nav-right"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i></a></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav></header><main class="layout" id="content-inner"><div id="post"><div id="post-info"><h1 class="post-title">Zookeeper基础知识</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-11-05T02:47:38.000Z" title="发表于 2022-11-05 10:47:38">2022-11-05</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-05-02T02:12:28.395Z" title="更新于 2023-05-02 10:12:28">2023-05-02</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">5.9k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>23分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="Zookeeper基础知识"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div><article class="post-content" id="article-container"><h2 id="Zookeeper-基础知识"><a href="#Zookeeper-基础知识" class="headerlink" title="Zookeeper 基础知识"></a>Zookeeper 基础知识</h2><h3 id="1-Zookeeper-入门"><a href="#1-Zookeeper-入门" class="headerlink" title="1.Zookeeper 入门"></a>1.Zookeeper 入门</h3><ol>
<li><p>概述</p>
<blockquote>
<p>Zookeeper 是一个开源的分布式的，为分布式框架提供协调服务的 Apache 项目</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E6%A6%82%E8%BF%B0.png" alt="概述"></p>
<blockquote>
<p>Zookeeper 工作机制</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E5%B7%A5%E4%BD%9C%E6%9C%BA%E5%88%B6.png" alt="工作机制"></p>
</li>
<li><p>特点</p>
<ul>
<li><p>Zookeeper</p>
<blockquote>
<p>一个领导者（Leader），多个跟随者（Follower）组成的集群</p>
</blockquote>
</li>
<li><p>集群中只要有<strong>半数以上</strong>节点存活，Zookeeper 集群就能正常服务</p>
<blockquote>
<p>Zookeeper 适合安装奇数台服务器</p>
</blockquote>
</li>
<li><p>全局数据一致</p>
<blockquote>
<p>每个 Server 保存一份相同的数据副本，Client 无论连接到哪个 Server，数据都是一致的</p>
</blockquote>
</li>
<li><p>更新请求顺序执行</p>
<blockquote>
<p>来自同一个 Client 的更新请求按其发送顺序依次执行</p>
</blockquote>
</li>
<li><p>数据更新原子性</p>
<blockquote>
<p>一次数据更新要么成功，要么失败</p>
</blockquote>
</li>
<li><p>实时性</p>
<blockquote>
<p>在一定时间范围内，Client 能读到最新数据</p>
</blockquote>
</li>
</ul>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E7%89%B9%E7%82%B9.png" alt="特点"></p>
</li>
<li><p>数据结构</p>
<blockquote>
<p>ZooKeeper 数据模型的结构与 Unix 文件系统很类似</p>
<p>整体上可以看作是一棵树，每个节点称做一个 ZNode</p>
<p>每一个 ZNode 默认能够存储 1MB 的数据，每个 ZNode 都可以通过其路径唯一标识</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.png" alt="数据结构"></p>
</li>
<li><p><strong>应用场景</strong></p>
<blockquote>
<p>提供的服务包括</p>
<p><strong>统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡</strong>等</p>
</blockquote>
<ul>
<li><p>统一命名服务</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E7%BB%9F%E4%B8%80%E5%91%BD%E5%90%8D%E6%9C%8D%E5%8A%A1.png" alt="统一命名服务"></p>
</li>
<li><p>统一配置管理</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E7%BB%9F%E4%B8%80%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86.png" alt="统一配置管理"></p>
</li>
<li><p>统一集群管理</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E7%BB%9F%E4%B8%80%E9%9B%86%E7%BE%A4%E7%AE%A1%E7%90%86.png" alt="统一集群管理"></p>
</li>
<li><p>服务器节点动态上下线</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%8A%A8%E6%80%81%E4%B8%8A%E4%B8%8B%E7%BA%BF.png" alt="服务器动态上下线"></p>
</li>
<li><p>软负载均衡</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E8%BD%AF%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1.png" alt="软负载均衡"></p>
</li>
</ul>
</li>
<li><p>下载地址</p>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://zookeeper.apache.org/">官网</a></p>
<p>要下载带 <code>bin</code> 的压缩包</p>
<p>带 <code>bin</code> 的压缩包是真正的标准压缩包</p>
<p>而不带 <code>bin</code> 的压缩包是源码压缩包</p>
</blockquote>
</li>
</ol>
<h3 id="2-Zookeeper-安装"><a href="#2-Zookeeper-安装" class="headerlink" title="2.Zookeeper 安装"></a>2.Zookeeper 安装</h3><ol>
<li><p>本地模式安装</p>
<ul>
<li><p>安装 JDK</p>
</li>
<li><p>下载压缩包 <code>apache-zookeeper-3.7.1-bin.tar.gz</code></p>
</li>
<li><p>上传到 Linux 并解压到 <code>/opt/module</code></p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">tar -zxvf apache-zookeeper-3.7.1-bin.tar.gz -C /opt/module/</span><br></pre></td></tr></table></figure>
</li>
<li><p>修改名称</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">mv apache-zookeeper-3.7.1-bin/ zookeeper-3.7.1/</span><br></pre></td></tr></table></figure>
</li>
<li><p>修改配置文件 <code>zookeeper-3.7.1/conf/zoo.cfg</code></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cp zoo_sample.cfg zoo.cfg</span><br><span class="line">vim zoo.cfg</span><br></pre></td></tr></table></figure>

<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">主要修改以下参数</span></span><br><span class="line">dataDir=/opt/module/zookeeper-3.7.1/zkData</span><br></pre></td></tr></table></figure>
</li>
<li><p>在 <code>/opt/module/zookeeper-3.7.1/</code> 这个目录上创建 <code>zkData</code> 文件夹</p>
</li>
<li><p>启动 Zookeeper 服务端</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[zcm@hadoop102 zookeeper-3.7.1]$ bin/zkServer.sh start</span><br></pre></td></tr></table></figure>
</li>
<li><p>jps 查看状态</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">3970 QuorumPeerMain</span><br><span class="line">4013 Jps</span><br></pre></td></tr></table></figure>
</li>
<li><p>查看 Zookeeper 状态</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[zcm@hadoop102 zookeeper-3.7.1]$ bin/zkServer.sh status</span><br></pre></td></tr></table></figure>
</li>
<li><p>启动 Zookeeper 客户端</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[zcm@hadoop102 zookeeper-3.7.1]$ bin/zkCli.sh</span><br></pre></td></tr></table></figure>
</li>
<li><p>关闭 Zookeeper 客户端</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[zk: localhost:2181(CONNECTED) 0] quit</span><br></pre></td></tr></table></figure>
</li>
<li><p>关闭 Zookeeper 服务端</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[zcm@hadoop102 zookeeper-3.7.1]$ bin/zkServer.sh stop</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>配置文件解读</p>
<blockquote>
<p>Zookeepe r中的配置文件 <code>zoo.cfg</code> 中参数含义解读如下</p>
</blockquote>
<ul>
<li><p>tickTime &#x3D; 2000</p>
<blockquote>
<p>通信心跳时间，Zookeeper 服务器与客户端心跳时间，单位毫秒</p>
</blockquote>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/tickTime.png" alt="tickTime"></p>
</li>
<li><p>initLimit &#x3D; 10</p>
<blockquote>
<p>LF 初始通信时限</p>
<p>Leader 和 Follower 初始连接时能容忍的最多心跳数（tickTime 的数量）</p>
</blockquote>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/leader-follower.png" alt="leader-follower"></p>
</li>
<li><p>syncLimit &#x3D; 5</p>
<blockquote>
<p>LF 同步通信时限</p>
<p>Leader 和 Follower 之间通信时间如果超过 <code>syncLimit * tickTime</code>，Leader 认为 Follwer 死掉，从服务器列表中删除 Follwer</p>
</blockquote>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/leader-follower.png" alt="leader-follower"></p>
</li>
<li><p>dataDir</p>
<blockquote>
<p>保存 Zookeeper 中的数据</p>
<p>默认的 tmp 目录，容易被 Linux 系统定期删除，所以一般不用默认的 tmp 目录</p>
</blockquote>
</li>
<li><p>clientPort &#x3D; 2181</p>
<blockquote>
<p>客户端连接端口，通常不做修改</p>
</blockquote>
</li>
</ul>
</li>
</ol>
<h3 id="3-Zookeeper-集群"><a href="#3-Zookeeper-集群" class="headerlink" title="3.Zookeeper 集群"></a>3.Zookeeper 集群</h3><ol>
<li><p>集群规划</p>
<blockquote>
<p>在 hadoop102、hadoop103 和 hadoop104 三个节点上都部署 Zookeeper</p>
<p>思考：如果是 10 台服务器，需要部署多少台 Zookeeper ？</p>
</blockquote>
</li>
<li><p>安装集群（在 <code>2.1 本地安装模式</code> 的基础上）</p>
<ul>
<li><p>将 Zookeeper 分发到 hadoop103、hadoop104</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">xsync zookeeper-3.7.1/</span><br></pre></td></tr></table></figure>
</li>
<li><p>配置服务器编号</p>
<blockquote>
<p>在 zkData 路径下创建 <code>myid</code></p>
<p>hadoop102 对应文件内容为 2</p>
<p>hadoop103 对应文件内容为 3</p>
<p>hadoop104 对应文件内容为 4</p>
</blockquote>
</li>
<li><p>修改配置文件</p>
<blockquote>
<p>增加如下配置</p>
</blockquote>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment">######################cluster##########################</span></span></span><br><span class="line">server.2=hadoop102:2888:3888</span><br><span class="line">server.3=hadoop103:2888:3888</span><br><span class="line">server.4=hadoop104:2888:3888</span><br></pre></td></tr></table></figure>

<blockquote>
<p>server.A&#x3D;B:C:D</p>
</blockquote>
<ul>
<li>A 是一个数字，表示这个是第几号服务器</li>
<li>集群模式下配置一个文件 myid，这个文件在 dataDir 目录下，这个文件里面有一个数据<br>就是 A 的值，Zookeeper 启动时读取此文件，拿到里面的数据与 zoo.cfg 里面的配置信息比较从而判断到底是哪个 server</li>
<li>B 是这个服务器的地址</li>
<li>C 是这个服务器 Follower 与集群中的 Leader 服务器交换信息的端口</li>
<li>D 是万一集群中的 Leader 服务器挂了，需要一个端口来重新进行选举，选出一个新的Leader，而这个端口就是用来执行选举时服务器相互通信的端口</li>
</ul>
</li>
<li><p>分发配置文件</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">xsync zoo.cfg</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>集群操作</p>
<ul>
<li><p>分别启动</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[atguigu@hadoop102 zookeeper-3.7.1]$ bin/zkServer.sh start</span><br><span class="line">[atguigu@hadoop103 zookeeper-3.7.1]$ bin/zkServer.sh start</span><br><span class="line">[atguigu@hadoop104 zookeeper-3.7.1]$ bin/zkServer.sh start</span><br></pre></td></tr></table></figure>
</li>
<li><p>查看状态</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[atguigu@hadoop102 zookeeper-3.7.1]$ bin/zkServer.sh status</span><br><span class="line">Mode: follower</span><br><span class="line"></span><br><span class="line">[atguigu@hadoop103 zookeeper-3.7.1]$ bin/zkServer.sh status</span><br><span class="line">Mode: leader</span><br><span class="line"></span><br><span class="line">[atguigu@hadoop104 zookeeper-3.7.1]$ bin/zkServer.sh status</span><br><span class="line">Mode: follower</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p><strong>选举机制（面试重点）</strong></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E9%80%89%E4%B8%BE1.png" alt="选举1"></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E9%80%89%E4%B8%BE2.png" alt="选举2"></p>
</li>
<li><p>集群启动 &#x2F; 停止脚本</p>
<blockquote>
<p>在 hadoop102 的 <code>/home/zcm/bin</code> 目录下创建脚本 <code>zk.sh</code></p>
</blockquote>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">!/bin/bash</span></span><br><span class="line">case $1 in</span><br><span class="line">&quot;start&quot;)&#123;</span><br><span class="line">	for i in hadoop102 hadoop103 hadoop104</span><br><span class="line">	do</span><br><span class="line">		echo ---------- zookeeper $i 启动 ------------</span><br><span class="line">		ssh $i &quot;/opt/module/zookeeper-3.7.1/bin/zkServer.sh start&quot;</span><br><span class="line">	done</span><br><span class="line">&#125;;;</span><br><span class="line">&quot;stop&quot;)&#123;</span><br><span class="line">	for i in hadoop102 hadoop103 hadoop104</span><br><span class="line">	do</span><br><span class="line">		echo ---------- zookeeper $i 停止 ------------</span><br><span class="line">		ssh $i &quot;/opt/module/zookeeper-3.7.1/bin/zkServer.sh stop&quot;</span><br><span class="line">	done</span><br><span class="line">&#125;;;</span><br><span class="line">&quot;status&quot;)&#123;</span><br><span class="line">	for i in hadoop102 hadoop103 hadoop104</span><br><span class="line">	do</span><br><span class="line">		echo ---------- zookeeper $i 状态 ------------</span><br><span class="line">		ssh $i &quot;/opt/module/zookeeper-3.7.1/bin/zkServer.sh status&quot;</span><br><span class="line">	done</span><br><span class="line">&#125;;;</span><br><span class="line">esac</span><br></pre></td></tr></table></figure>

<blockquote>
<p>增加执行权限</p>
</blockquote>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">chmod +x zk.sh</span><br></pre></td></tr></table></figure>
</li>
<li><p>客户端命令行操作</p>
<ul>
<li><p>启动客户端</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">bin/zkCli.sh -server</span><br></pre></td></tr></table></figure>
</li>
<li><p>基本语法</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95.png" alt="基本语法"></p>
</li>
</ul>
</li>
<li><p>znode 节点</p>
<ul>
<li><p>查看详细信息</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[zk: localhost:2181(CONNECTED) 2] ls -s /</span><br></pre></td></tr></table></figure>

<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[zookeeper]</span><br><span class="line">cZxid = 0x0</span><br><span class="line">ctime = Thu Jan 01 08:00:00 CST 1970</span><br><span class="line">mZxid = 0x0</span><br><span class="line">mtime = Thu Jan 01 08:00:00 CST 1970</span><br><span class="line">pZxid = 0x0</span><br><span class="line">cversion = -1</span><br><span class="line">dataVersion = 0</span><br><span class="line">aclVersion = 0</span><br><span class="line">ephemeralOwner = 0x0</span><br><span class="line">dataLength = 0</span><br><span class="line">numChildren = 1</span><br></pre></td></tr></table></figure>

<ul>
<li><p><code>czxid</code>：创建节点的事务 zxid</p>
<blockquote>
<p>每次修改 ZooKeeper 状态都会产生一个 ZooKeeper 事务 ID</p>
<p>事务 ID 是 ZooKeeper 中所有修改总的次序</p>
<p>每次修改都有唯一的 zxid，如果 zxid1 小于 zxid2，那么 zxid1 在 zxid2 之前发生</p>
</blockquote>
</li>
<li><p><code>ctime</code>：znode 被创建的毫秒数（从 1970 年开始）</p>
</li>
<li><p><code>mzxid</code>：znode 最后更新的事务 zxid</p>
</li>
<li><p><code>mtime</code>：znode 最后修改的毫秒数（从 1970 年开始）</p>
</li>
<li><p><code>pZxid</code>：znode 最后更新的子节点 zxid</p>
</li>
<li><p><code>cversion</code>：znode 子节点变化号，znode 子节点修改次数</p>
</li>
<li><p><code>dataversion</code>：znode 数据变化号</p>
</li>
<li><p><code>aclVersion</code>：znode 访问控制列表的变化号</p>
</li>
<li><p><code>ephemeralOwner</code>：如果是临时节点，这个是 znode 拥有者的 session id。如果不是<br>临时节点则是 0</p>
</li>
<li><p><code>dataLength</code>：znode 的数据长度</p>
</li>
<li><p><code>numChildren</code>：znode 子节点数量</p>
</li>
</ul>
</li>
<li><p><strong>节点类型（持久 vs  短暂 &#x2F; 有序号 vs 无序号)</strong></p>
<blockquote>
<p>可两两组合：持久有序号、持久无序号、短暂有序号、短暂无序号</p>
<p><strong>创建节点时，要赋值</strong></p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E8%8A%82%E7%82%B9%E7%B1%BB%E5%9E%8B.png" alt="节点类型"></p>
<ul>
<li><p>创建 2 个普通节点（持久 + 无序号）</p>
<p><code>create ...</code></p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E5%88%9B%E5%BB%BA%E8%8A%82%E7%82%B91.png" alt="创建节点1"></p>
</li>
<li><p>创建带序号的节点 （持久 + 有序号）</p>
<p><code>create -s</code></p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E5%88%9B%E5%BB%BA%E8%8A%82%E7%82%B92.png" alt="创建节点2"></p>
</li>
<li><p>创建短暂节点 （短暂 + 无序号）</p>
<p><code>create -e</code></p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E5%88%9B%E5%BB%BA%E8%8A%82%E7%82%B93.png" alt="创建节点3"></p>
</li>
<li><p>创建短暂节点（短暂 + 有序号）</p>
<p><code>create -e -s</code></p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E5%88%9B%E5%BB%BA%E8%8A%82%E7%82%B93.png" alt="创建节点3"></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>监听器原理</p>
<blockquote>
<p>客户端注册监听它关心的目录节点，当目录节点发生变化（数据改变、节点删除、子目录节点增加删除）时，ZooKeeper 会通知客户端</p>
<p>监听机制保证 ZooKeeper 保存的任何的数据的任何改变都能快速的响应到监听了该节点的应用程序</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E7%9B%91%E5%90%AC%E5%99%A8%E5%8E%9F%E7%90%86.png" alt="监听器原理"></p>
<ul>
<li><p>监听节点数据的变化</p>
<ul>
<li><p>在 hadoop104 主机上注册监听 <code>/sanguo</code> 节点数据变化</p>
<p><code>get -w /nodename</code></p>
</li>
<li><p>在 hadoop103 主机上修改 <code>/sanguo</code> 节点的数据</p>
<p><code>set /nodename newValue</code></p>
</li>
<li><p>观察 hadoop104 主机收到数据变化的监听</p>
</li>
</ul>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E7%9B%91%E5%90%AC1.png" alt="监听1"></p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E7%9B%91%E5%90%AC2.png" alt="监听2"></p>
<blockquote>
<p>在 hadoop103 再多次修改 &#x2F;sanguo 的值，hadoop104 上不会再收到监听</p>
<p><strong>因为注册一次，只能监听一次。想再次监听，需要再次注册</strong></p>
</blockquote>
</li>
<li><p>监听节点增减的变化</p>
<ul>
<li>在 hadoop104 主机上注册监听 <code>/sanguo</code> 节点的子节点变化</li>
<li>在 hadoop103 主机 <code>/sanguo</code> 节点上创建子节点</li>
<li>观察 hadoop104 主机收到子节点变化的监听</li>
</ul>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E7%9B%91%E5%90%AC3.png" alt="监听3"></p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E7%9B%91%E5%90%AC4.png" alt="监听4"></p>
<blockquote>
<p>同上，<strong>注册一次，只能监听一次</strong></p>
</blockquote>
</li>
<li><p>删除节点：<code>delete /nodename</code></p>
</li>
<li><p>递归删除节点：<code>deleteall /nodename</code></p>
</li>
<li><p>查看节点状态：<code>stat /nodename</code></p>
</li>
</ul>
</li>
<li><p>客户端 API 操作</p>
<blockquote>
<p>前提：保证 hadoop102、hadoop103、hadoop104 服务器上 Zookeeper 集群服务端启动</p>
</blockquote>
<ul>
<li><p>在 IDEA 创建一个 Maven 工程：zookeeper</p>
</li>
<li><p>添加 pom 文件</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>4.13<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.logging.log4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>log4j-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.14.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.zookeeper<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>zookeeper<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.5.7<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>拷贝 <code>log4j.properties</code> 文件到项目根目录</p>
<figure class="highlight properties"><table><tr><td class="code"><pre><span class="line"><span class="attr">log4j.rootLogger</span>=<span class="string">INFO, stdout  </span></span><br><span class="line"><span class="attr">log4j.appender.stdout</span>=<span class="string">org.apache.log4j.ConsoleAppender  </span></span><br><span class="line"><span class="attr">log4j.appender.stdout.layout</span>=<span class="string">org.apache.log4j.PatternLayout  </span></span><br><span class="line"><span class="attr">log4j.appender.stdout.layout.ConversionPattern</span>=<span class="string">%d %p [%c] - %m%n  </span></span><br><span class="line"><span class="attr">log4j.appender.logfile</span>=<span class="string">org.apache.log4j.FileAppender  </span></span><br><span class="line"><span class="attr">log4j.appender.logfile.File</span>=<span class="string">target/spring.log  </span></span><br><span class="line"><span class="attr">log4j.appender.logfile.layout</span>=<span class="string">org.apache.log4j.PatternLayout  </span></span><br><span class="line"><span class="attr">log4j.appender.logfile.layout.ConversionPattern</span>=<span class="string">%d %p [%c] - %m%n </span></span><br></pre></td></tr></table></figure>
</li>
<li><p>创建包名 <code>com.rg.zk</code></p>
</li>
<li><p>创建 Zookeeper 客户端 <code>zkClient</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">zkClient</span> &#123;</span><br><span class="line">    <span class="comment">//注意逗号后面不能有空格.</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">connectString</span> <span class="operator">=</span> <span class="string">&quot;hadoop102:2181,hadoop103:2181,hadoop104:2181&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">sessionTimeout</span> <span class="operator">=</span> <span class="number">5000</span>;</span><br><span class="line">    <span class="keyword">private</span> ZooKeeper zkClient;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * connectString:要连接的Zookeeper的客户端.</span></span><br><span class="line"><span class="comment">         * sessionTimeout:超时时间</span></span><br><span class="line"><span class="comment">         * Watcher:监听器</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        zkClient = <span class="keyword">new</span> <span class="title class_">ZooKeeper</span>(connectString, sessionTimeout, <span class="keyword">new</span> <span class="title class_">Watcher</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">process</span><span class="params">(WatchedEvent watchedEvent)</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;--------------------------------&quot;</span>);</span><br><span class="line">                <span class="comment">//这里意思就是你只要执行zk的api指令，就会走监听器重写的方法，最后加一个延迟，主线程睡眠但是监听器还在</span></span><br><span class="line">                <span class="comment">//本质:是让监听器线程一直存在着...</span></span><br><span class="line">                <span class="comment">//再次启动监听...</span></span><br><span class="line">                List &lt;String&gt; children = <span class="literal">null</span>;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    children = zkClient.getChildren(<span class="string">&quot;/&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (KeeperException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">for</span> (String child : children) &#123;</span><br><span class="line">                    System.out.println(child);</span><br><span class="line">                &#125;</span><br><span class="line">                System.out.println(<span class="string">&quot;--------------------------------&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">     <span class="comment">// 1.创建节点</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">create</span><span class="params">()</span> <span class="keyword">throws</span> KeeperException, InterruptedException &#123;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 参数 1：要创建的节点的路径；</span></span><br><span class="line"><span class="comment">         * 参数 2：节点数据 ；</span></span><br><span class="line"><span class="comment">         * 参数 3：节点权限 ；</span></span><br><span class="line"><span class="comment">         * 参数 4：节点的类型</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">nodeCreated</span> <span class="operator">=</span> zkClient.create(<span class="string">&quot;/atguigu&quot;</span>, <span class="string">&quot;ss.avi&quot;</span>.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 2.获取子节点并监听    </span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">getChildRen</span><span class="params">()</span> <span class="keyword">throws</span> KeeperException, InterruptedException &#123;</span><br><span class="line">        <span class="comment">//true:使用init中创建的监听器. 每次出现变化,将重新调用监听器中的 方法.  也可以自定一个监听器.</span></span><br><span class="line">        <span class="comment">//监听某个路径的节点变化情况</span></span><br><span class="line">        List &lt;String&gt; children = zkClient.getChildren(<span class="string">&quot;/&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">        <span class="keyword">for</span> (String child : children) &#123;</span><br><span class="line">            System.out.println(child);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//延时 让其一直监听</span></span><br><span class="line">        Thread.sleep(Long.MAX_VALUE);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 3.判断 Znode 是否存在</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exist</span><span class="params">()</span> <span class="keyword">throws</span> KeeperException, InterruptedException &#123;</span><br><span class="line">        <span class="type">Stat</span> <span class="variable">stat</span> <span class="operator">=</span> zkClient.exists(<span class="string">&quot;/atguigu&quot;</span>, <span class="literal">false</span>);</span><br><span class="line">        System.out.println(stat==<span class="literal">null</span> ? <span class="string">&quot;not exist &quot;</span>:<span class="string">&quot;exist&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>客户端向服务端写数据流程</p>
<ul>
<li><p>直接访问 leader</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E5%86%99%E6%B5%81%E7%A8%8B1.png" alt="写流程1"></p>
<blockquote>
<p>一共三台 zk 服务器，超过半数写完成即可向客户端返回 ack（见步骤 4）</p>
</blockquote>
</li>
<li><p>访问 follower</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E5%86%99%E6%B5%81%E7%A8%8B2.png" alt="写流程2"></p>
<blockquote>
<p>一共三台 zk 服务器，超过半数写完成即可向客户端返回 ack（见步骤 5）</p>
</blockquote>
</li>
</ul>
</li>
</ol>
<h3 id="4-服务器动态上下线监听案例"><a href="#4-服务器动态上下线监听案例" class="headerlink" title="4.服务器动态上下线监听案例"></a>4.服务器动态上下线监听案例</h3><ol>
<li><p>需求</p>
<blockquote>
<p>某分布式系统中，主节点可以有多台，可以动态上下线，任意一台客户端都能实时感知到主节点服务器的上下线</p>
<p>服务器上线的过程就是 Zookeeper 集群创建节点的过程</p>
<p>服务器和客户端相对于 Zookeeper 都是<code>客户端</code>，只不过服务器是创建节点的操作，客户端是监听节点的操作（一旦那个节点不存在了，下次就不去访问这个节点了）</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%8A%A8%E6%80%81%E4%B8%8A%E4%B8%8B%E7%BA%BF.png" alt="服务器动态上下线"></p>
</li>
<li><p>实现</p>
<ul>
<li><p>先在集群上创建 <code>/servers</code> 节点</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">create /servers &quot;servers&quot;</span><br></pre></td></tr></table></figure>
</li>
<li><p>在 IDEA 中创建包：<code>com.rg.case1</code></p>
</li>
<li><p>服务器端向 Zookeeper 注册代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DistributeServer</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">connectString</span>  <span class="operator">=</span> <span class="string">&quot;hadoop102:2181,hadoop103:2181,hadoop104:2181&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">sessionTimeout</span> <span class="operator">=</span> <span class="number">2000</span>;</span><br><span class="line">    <span class="keyword">private</span> ZooKeeper zk;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, KeeperException, InterruptedException &#123;</span><br><span class="line">        <span class="type">DistributeServer</span> <span class="variable">server</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DistributeServer</span>();</span><br><span class="line">        <span class="comment">//1.获取zk连接 ==&gt;连接zk客户端.</span></span><br><span class="line">        server.getConnect();</span><br><span class="line">        <span class="comment">//2.注册服务器到zk集群  在/servers下创建节点</span></span><br><span class="line">        server.regist(args[<span class="number">0</span>]);</span><br><span class="line">        <span class="comment">//3.启动业务逻辑(睡觉)</span></span><br><span class="line">        server.business();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">business</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        Thread.sleep(Long.MAX_VALUE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">regist</span><span class="params">(String hostname)</span> <span class="keyword">throws</span> KeeperException, InterruptedException &#123;</span><br><span class="line">        <span class="comment">// 节点类型应该是 临时的(上线创建节点,下线节点消失),有序的(可以得知服务器上线的顺序)</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">create</span> <span class="operator">=</span> zk.create(<span class="string">&quot;/servers/&quot;</span> + hostname, hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);</span><br><span class="line">        System.out.println(hostname+<span class="string">&quot;is online&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">getConnect</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">//ctrl+alt+f</span></span><br><span class="line">        zk = <span class="keyword">new</span> <span class="title class_">ZooKeeper</span>(connectString, sessionTimeout, <span class="keyword">new</span> <span class="title class_">Watcher</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">process</span><span class="params">(WatchedEvent event)</span> &#123;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>客户端代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DistributeClient</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">connectString</span>  <span class="operator">=</span> <span class="string">&quot;hadoop102:2181,hadoop103:2181,hadoop104:2181&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">sessionTimeout</span> <span class="operator">=</span> <span class="number">2000</span>;</span><br><span class="line">    <span class="keyword">private</span> ZooKeeper zk;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, InterruptedException, KeeperException &#123;</span><br><span class="line">        <span class="type">DistributeClient</span> <span class="variable">client</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DistributeClient</span>();</span><br><span class="line">        <span class="comment">//1.获取zk连接</span></span><br><span class="line">        client.getConnect();</span><br><span class="line">        <span class="comment">//2.监听 /servers/下面子节点的增加和删除==&gt;监听服务器的上线下线情况</span></span><br><span class="line">        client.getServerList();</span><br><span class="line">        <span class="comment">//3.业务逻辑(睡觉)</span></span><br><span class="line">        client.business();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">getServerList</span><span class="params">()</span> <span class="keyword">throws</span> KeeperException, InterruptedException &#123;</span><br><span class="line">        <span class="comment">//进行注册监听器..</span></span><br><span class="line">        List &lt;String&gt; children = zk.getChildren(<span class="string">&quot;/servers&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">        <span class="comment">//存放 /servers下的节点</span></span><br><span class="line">        List &lt;String&gt; servers = <span class="keyword">new</span> <span class="title class_">ArrayList</span> &lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (String child : children) &#123;</span><br><span class="line">            <span class="comment">//获取该节点上的内容 不适用监听器</span></span><br><span class="line">            <span class="type">byte</span>[] data = zk.getData(<span class="string">&quot;/servers/&quot;</span> + child, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">            servers.add(<span class="keyword">new</span> <span class="title class_">String</span>(data));</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(servers);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">business</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        Thread.sleep(Long.MAX_VALUE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">getConnect</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">//ctrl+alt+f</span></span><br><span class="line">        zk = <span class="keyword">new</span> <span class="title class_">ZooKeeper</span>(connectString, sessionTimeout, <span class="keyword">new</span> <span class="title class_">Watcher</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">process</span><span class="params">(WatchedEvent event)</span> &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">//注册一次监听一次..</span></span><br><span class="line">                    getServerList();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (KeeperException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>测试</p>
<ul>
<li><p>在 Linux 命令行上操作增加减少服务器</p>
<ul>
<li><p>启动 DistributeClient 客户端</p>
</li>
<li><p>在 hadoop102 上 zk 的客户端 <code>/servers</code> 目录上创建临时带序号节点</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[zk: localhost:2181(CONNECTED) 1] create -e -s /servers/hadoop102 &quot;hadoop102&quot;</span><br><span class="line">[zk: localhost:2181(CONNECTED) 2] create -e -s /servers/hadoop103 &quot;hadoop103&quot;</span><br></pre></td></tr></table></figure>
</li>
<li><p>观察 IDEA 控制台输出</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[hadoop102, hadoop103]</span><br></pre></td></tr></table></figure>
</li>
<li><p>执行删除操作</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[zk: localhost:2181(CONNECTED) 8] delete /servers/hadoop1020000000000</span><br></pre></td></tr></table></figure>
</li>
<li><p>观察 IDEA  控制台输出</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[hadoop103]</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>在 IDEA 上操作增加减少服务器</p>
<ul>
<li><p>启动 DistributeClient 客户端（如果已经启动过，不需要重启）</p>
</li>
<li><p>启动 DistributeServer 服务（带参数 <code>hadoop102</code>）</p>
</li>
<li><p>观察 DistributeServer 控制台</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">hadoop102 is online</span><br></pre></td></tr></table></figure>
</li>
<li><p>观察 DistributeClient 控制台</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[hadoop102]</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
</li>
</ol>
<h3 id="5-Zookeeper-分布式锁案例"><a href="#5-Zookeeper-分布式锁案例" class="headerlink" title="5.Zookeeper 分布式锁案例"></a>5.Zookeeper 分布式锁案例</h3><ol>
<li><p>什么是分布式锁</p>
<blockquote>
<p>比如说进程 1 在使用该资源的时候，会先去获得锁，进程 1 获得锁以后会对该资源保持独占，这样其他进程就无法访问该资源</p>
<p>进程 1 用完该资源以后就将锁释放掉，让其他进程来获得锁</p>
<p>通过这个锁机制就能保证了分布式系统中多个进程能够有序的访问该临界资源</p>
<p><strong>把这个分布式环境下的这个锁叫作分布式锁</strong></p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E6%A1%88%E4%BE%8B.png" alt="分布式锁案例"></p>
</li>
<li><p>原生 Zookeeper 实现</p>
<ul>
<li><p>实现</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DistributedLock</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">connectString</span> <span class="operator">=</span> <span class="string">&quot;hadoop102:2181,hadoop103:2181,hadoop104:2181&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">sessionTimeout</span> <span class="operator">=</span> <span class="number">2000</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ZooKeeper zk;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//CountDownLatch 用来等待 连接成功</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">CountDownLatch</span> <span class="variable">connectLatch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">private</span> <span class="type">CountDownLatch</span> <span class="variable">waitLatch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">private</span> String currentMode;</span><br><span class="line">    <span class="keyword">private</span> String waitPath;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//分布式锁的初始化</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DistributedLock</span><span class="params">()</span> <span class="keyword">throws</span> IOException, InterruptedException, KeeperException &#123;</span><br><span class="line">        <span class="comment">//获取连接</span></span><br><span class="line">         zk = <span class="keyword">new</span> <span class="title class_">ZooKeeper</span>(connectString, sessionTimeout, <span class="keyword">new</span> <span class="title class_">Watcher</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">process</span><span class="params">(WatchedEvent event)</span> &#123;</span><br><span class="line">                <span class="comment">//connection 如果连接上zk 可以释放</span></span><br><span class="line">                <span class="comment">// 如果监听的状态 是 连接上的状态,则释放connectLatch,继续往下执行.</span></span><br><span class="line">                <span class="keyword">if</span>(event.getState()==Event.KeeperState.SyncConnected)&#123;</span><br><span class="line">                    connectLatch.countDown();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">//waitLatch 需要释放</span></span><br><span class="line">                <span class="comment">//如果监听到了 监听路径的节点删除操作. 并且该操作的路径是当前节点的上一个节点,则释放waitLatch</span></span><br><span class="line">                <span class="keyword">if</span>(event.getType()==Event.EventType.NodeDeleted &amp;&amp; event.getPath().equals(waitPath))&#123;</span><br><span class="line">                    waitLatch.countDown();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">         <span class="comment">//等待zk正常连接后,往下走程序</span></span><br><span class="line">        connectLatch.await();</span><br><span class="line">        <span class="comment">//判断根节点 /locks 是否存在</span></span><br><span class="line">        <span class="type">Stat</span> <span class="variable">stat</span> <span class="operator">=</span> zk.exists(<span class="string">&quot;/locks&quot;</span>, <span class="literal">false</span>);</span><br><span class="line">        <span class="keyword">if</span>(stat==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="comment">//创建下一个根节点</span></span><br><span class="line">            zk.create(<span class="string">&quot;/locks&quot;</span>, <span class="string">&quot;locks&quot;</span>.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//对zk加锁</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">zklock</span><span class="params">()</span> <span class="keyword">throws</span> KeeperException, InterruptedException &#123;</span><br><span class="line">        <span class="comment">//创建对应的临时带序号节点(目的是对资源进行操作.)</span></span><br><span class="line">        currentMode = zk.create(<span class="string">&quot;/locks/&quot;</span> + <span class="string">&quot;seq-&quot;</span>, <span class="literal">null</span>, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);</span><br><span class="line">        <span class="comment">//判断创建的节点是否是最小的序号节点,如果是获取到锁; 如果不是,监听序号前一个节点.</span></span><br><span class="line">        List &lt;String&gt; children = zk.getChildren(<span class="string">&quot;/locks&quot;</span>, <span class="literal">false</span>);</span><br><span class="line">        <span class="comment">//如果children只有一个值, 那就直接获取锁;  如果有多个节点,则需要判断谁最小.</span></span><br><span class="line">        <span class="keyword">if</span>(children.size()==<span class="number">1</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            Collections.sort(children);</span><br><span class="line">            <span class="comment">//获取节点名称 seq-000000</span></span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * substring(int biginIndex)</span></span><br><span class="line"><span class="comment">             * substring(int biginIndex,int endIndex)</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">thisNode</span> <span class="operator">=</span> currentMode.substring(<span class="string">&quot;/locks/&quot;</span>.length());</span><br><span class="line">            <span class="comment">//通过seq-000000获取该节点在children集合的位置</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> children.indexOf(thisNode);</span><br><span class="line">            <span class="comment">//判断</span></span><br><span class="line">            <span class="keyword">if</span>(index==-<span class="number">1</span>)&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;数据异常...&quot;</span>);</span><br><span class="line">            &#125;<span class="keyword">else</span> <span class="keyword">if</span>(index == <span class="number">0</span>)&#123;</span><br><span class="line">                <span class="comment">//如果当前的是序号最小的节点,则直接获取锁</span></span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;<span class="comment">//当前的节点并不是序号最小的</span></span><br><span class="line">                <span class="comment">//需要监听它前一个结点的变化</span></span><br><span class="line">                waitPath = <span class="string">&quot;/locks/&quot;</span> + children.get(index - <span class="number">1</span>);</span><br><span class="line">                zk.getData(waitPath,<span class="literal">true</span>,<span class="literal">null</span>);</span><br><span class="line">                <span class="comment">//等待前一个节点操作完成,监听结束,本节点再获取锁.</span></span><br><span class="line">                waitLatch.await();</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//解锁</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">unZkLock</span><span class="params">()</span> <span class="keyword">throws</span> KeeperException, InterruptedException &#123;</span><br><span class="line">        <span class="comment">// 操作处理完毕要解锁----删除当前节点.</span></span><br><span class="line">        zk.delete(<span class="built_in">this</span>.currentMode,-<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>测试</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DistributedLockTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException, IOException, KeeperException &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">DistributedLock</span> <span class="variable">lock1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DistributedLock</span>();</span><br><span class="line">        <span class="keyword">final</span> <span class="type">DistributedLock</span> <span class="variable">lock2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DistributedLock</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    lock1.zklock();</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程1启动, 获取到锁&quot;</span>);</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程1使用资源中...&quot;</span>);</span><br><span class="line">                    Thread.sleep(<span class="number">5</span>*<span class="number">1000</span>);</span><br><span class="line">                    lock1.unZkLock();</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程1使用资源完毕,释放锁&quot;</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (KeeperException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    lock2.zklock();</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程2启动, 获取到锁&quot;</span>);</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程2使用资源中...&quot;</span>);</span><br><span class="line">                    Thread.sleep(<span class="number">5</span>*<span class="number">1000</span>);</span><br><span class="line">                    lock2.unZkLock();</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程2使用资源完毕,释放锁&quot;</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (KeeperException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>Curator 框架实现</p>
<blockquote>
<p><strong>Curator 是一个专门解决分布式锁的框架，解决了原生Java API 开发分布式遇到的问题</strong></p>
<p>原生的 Java API 开发存在以下问题</p>
</blockquote>
<ul>
<li>会话连接是异步的，需要自己去处理（比如使用 CountDownLatch）</li>
<li>Watch 需要重复注册，不然就不能生效</li>
<li>开发的复杂性还是比较高的</li>
<li>不支持多节点删除和创建。需要手动递归</li>
</ul>
<blockquote>
<p>Curator 使用步骤</p>
</blockquote>
<ul>
<li><p>添加依赖</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.curator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>curator-framework<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>4.3.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.curator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>curator-recipes<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>4.3.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.curator<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>curator-client<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>4.3.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>代码实现</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CuratorLockTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//创建分布式锁1</span></span><br><span class="line">        <span class="type">InterProcessMutex</span> <span class="variable">lock1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InterProcessMutex</span>(getCuratorFramework(),<span class="string">&quot;/locks&quot;</span>);</span><br><span class="line">        <span class="type">InterProcessMutex</span> <span class="variable">lock2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InterProcessMutex</span>(getCuratorFramework(),<span class="string">&quot;/locks&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    lock1.acquire();<span class="comment">//获取锁</span></span><br><span class="line">                    System.out.println(<span class="string">&quot;线程1获取到锁&quot;</span>);</span><br><span class="line"></span><br><span class="line">                    lock1.acquire();</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程1再次获取到锁&quot;</span>);</span><br><span class="line">                    Thread.sleep(<span class="number">5</span>*<span class="number">1000</span>);</span><br><span class="line">                    lock1.release();<span class="comment">//释放锁</span></span><br><span class="line">                    System.out.println(<span class="string">&quot;线程1释放锁..&quot;</span>);</span><br><span class="line">                    lock1.release();</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程1再次释放锁...&quot;</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    lock2.acquire();<span class="comment">//获取锁</span></span><br><span class="line">                    System.out.println(<span class="string">&quot;线程2获取到锁&quot;</span>);</span><br><span class="line"></span><br><span class="line">                    lock2.acquire();</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程2再次获取到锁&quot;</span>);</span><br><span class="line">                    Thread.sleep(<span class="number">5</span>*<span class="number">1000</span>);</span><br><span class="line">                    lock2.release();<span class="comment">//释放锁</span></span><br><span class="line">                    System.out.println(<span class="string">&quot;线程2释放锁..&quot;</span>);</span><br><span class="line">                    lock2.release();</span><br><span class="line">                    System.out.println(<span class="string">&quot;线程2再次释放锁...&quot;</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> CuratorFramework <span class="title function_">getCuratorFramework</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">ExponentialBackoffRetry</span> <span class="variable">policy</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ExponentialBackoffRetry</span>(<span class="number">3000</span>, <span class="number">3</span>);</span><br><span class="line">        <span class="type">CuratorFramework</span> <span class="variable">client</span> <span class="operator">=</span> CuratorFrameworkFactory.builder().connectString(<span class="string">&quot;hadoop102:2181,hadoop103:2181,hadoop104:2181&quot;</span>)</span><br><span class="line">                .connectionTimeoutMs(<span class="number">2000</span>) <span class="comment">//设置连接超时时间</span></span><br><span class="line">                .sessionTimeoutMs(<span class="number">2000</span>)</span><br><span class="line">                .retryPolicy(policy).build(); <span class="comment">//尝试策略</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//启动客户端</span></span><br><span class="line">        client.start();</span><br><span class="line">        System.out.println(<span class="string">&quot;Zookeeper启动成功..&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> client;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ol>
<h3 id="6-企业面试真题"><a href="#6-企业面试真题" class="headerlink" title="6.企业面试真题"></a>6.企业面试真题</h3><ol>
<li><p>Zookeeper 选举机制</p>
<blockquote>
<p>半数机制，超过半数的投票通过，即通过</p>
</blockquote>
<ul>
<li><p>第一次启动选举规则</p>
<blockquote>
<p>投票过半数时，服务器 id 大的胜出</p>
</blockquote>
</li>
<li><p>第二次启动选举规则</p>
<blockquote>
<p>EPOCH 大的直接胜出</p>
<p>EPOCH 相同，事务 id 大的胜出</p>
<p>事务 id 相同，服务器 id 大的胜出</p>
</blockquote>
</li>
</ul>
</li>
<li><p>生产集群至少安装多少 zk 合适</p>
<blockquote>
<p>安装奇数台</p>
<p>10 台服务器：3 台 zk<br>20 台服务器：5 台 zk<br>100 台服务器：11 台 zk<br>200 台服务器：11 台 zk</p>
</blockquote>
<blockquote>
<p><strong>服务器台数多：好处，提高可靠性；坏处：提高通信延时</strong></p>
</blockquote>
</li>
<li><p>常用命令</p>
<p><code>ls、get、create、delete</code></p>
</li>
</ol>
<h3 id="7-源码入门算法基础"><a href="#7-源码入门算法基础" class="headerlink" title="7.源码入门算法基础"></a>7.源码入门算法基础</h3><ol>
<li><p>拜占庭将军问题</p>
<blockquote>
<p>拜占庭将军问题是一个协议问题</p>
<p>拜占庭帝国军队的将军们必须全体一致的决定是否攻击某一支敌军</p>
<p>问题是这些将军在地理上是分隔开来的，并且将军中存在叛徒</p>
<p>叛徒可以任意行动以达到以下目标：</p>
<ul>
<li><p>欺骗某些将军采取进攻行动</p>
</li>
<li><p>促成一个不是所有将军都同意的决定，如当将军们不希望进攻时促成进攻行动</p>
</li>
<li><p>或者迷惑某些将军，使他们无法做出决定</p>
</li>
</ul>
<p>如果叛徒达到了这些目的之一，则任何攻击行动的结果都是注定要失败的，只有完全达成一致的努力才能获得胜利</p>
<p>拜占庭假设是对现实世界的模型化，由于硬件错误、网络拥塞或断开以及遭到恶意攻击，计算机和网络可能出现不可预料的行为</p>
</blockquote>
<blockquote>
<p>拜占庭将军问题最终想解决的是互联网交易、合作过程中的四个问题：</p>
<p><strong>区块链轻而易举地解决了拜占庭将军问题</strong></p>
</blockquote>
<ul>
<li>信息发送的身份追溯</li>
<li>信息的私密性</li>
<li>不可伪造的签名</li>
<li>发送信息的规则</li>
</ul>
</li>
<li><p>paxos 算法</p>
<ul>
<li><p>Paxos 算法作用</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/paxos1.png" alt="paxos1"></p>
</li>
<li><p>Paxos 算法描述</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/paxos2.png" alt="paxos2"></p>
</li>
<li><p>Paxos 算法流程</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/paxos3.png" alt="paxos3"></p>
</li>
<li><p>Paxos 算法举例</p>
<blockquote>
<p>举例 1</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/paxos%E4%B8%BE%E4%BE%8B1.png" alt="paxos举例1"></p>
<blockquote>
<p>举例 2</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/paxos%E4%B8%BE%E4%BE%8B2.png" alt="paxos举例2"></p>
<blockquote>
<p>举例 3</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/paxos%E4%B8%BE%E4%BE%8B3.png" alt="paxos举例3"></p>
<blockquote>
<p>造成这种情况的原因是系统中有一个以上的 Proposer，多个 Proposers 相互争夺 Acceptor，造成退退无法达成一致的情况</p>
<p>针对这种情况，一种改进的 Paxos 算法被提出：从系统中选出一个节点作为 Leader，只有 Leader 能够发起提案</p>
<p>这样，一次 Paxos 流程中只有一个Proposer，不会出现活锁的情况，此时只会出现举例 1 中情况</p>
</blockquote>
</li>
</ul>
</li>
<li><p>Zab 协议（Paxos 算法的改进）</p>
<blockquote>
<p>Zab 协议包括两种基本模式：消息广播、崩溃恢复</p>
</blockquote>
<ul>
<li><p>消息广播</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/zab1.png" alt="zab1"></p>
</li>
<li><p>崩溃恢复</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/zab2.png" alt="zab2"></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/zab3.png" alt="zab3"></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/zab4.png" alt="zab4"></p>
</li>
</ul>
</li>
<li><p>cap 理论</p>
<blockquote>
<p>CAP 理论告诉我们，一个分布式系统不可能同时满足以下三种</p>
</blockquote>
<ul>
<li><p>一致性（Consistency）</p>
<blockquote>
<p>在分布式环境中，一致性是指数据在多个副本之间是否能够保持数据一致的特性</p>
<p>在一致性的需求下，当一个系统在数据一致的状态下执行更新操作后，应该保证系统的数据仍然处于一致的状态</p>
</blockquote>
</li>
<li><p>可用性（Available）</p>
<blockquote>
<p>可用性是指系统提供的服务必须一直处于可用的状态，对于用户的每一个操作请求总是能够在有限的时间内返回结果</p>
</blockquote>
</li>
<li><p>分区容错性（P:PartitionTolerance）</p>
<blockquote>
<p>分布式系统在到任何网络分区故障的时候，仍然需要能够保证对外提供满足一致性和可用性的服务，除非是整个网络环境都发生了故障</p>
</blockquote>
</li>
</ul>
<blockquote>
<p>这三个基本需求，最多只能同时满足其中的两项，因为 P 是必须的，因此往往选择就在 CP 或者 AP 中</p>
<p><strong>ZooKeeper 保证的是 CP</strong></p>
</blockquote>
<ul>
<li>ZooKeeper 不能保证每次服务请求的可用性（在极端环境下，ZooKeeper 可能会丢弃一些请求，消费者程序需要重新请求才能获得结果）所以说 ZooKeeper 不能保证服务可用性</li>
<li>进行 Leader 选举时集群都是不可用</li>
</ul>
</li>
</ol>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="https://gitee.com/zcmmmm/zcmmmm">zcm</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://gitee.com/zcmmmm/zcmmmm/2022/11/05/zookeeper%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/">https://gitee.com/zcmmmm/zcmmmm/2022/11/05/zookeeper%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://gitee.com/zcmmmm/zcmmmm" target="_blank">无人深空</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/Zookeeper/">Zookeeper</a></div><div class="post_share"><div class="social-share" data-image="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/Zookeeper.png" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2022/11/10/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%9E%B6%E6%9E%84/"><img class="prev-cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/BigDataStructure/BigDataStructure.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">大数据框架</div></div></a></div><div class="next-post pull-right"><a href="/2022/10/30/kafka%E9%9D%A2%E7%BB%8F/"><img class="next-cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Kafka/kafka.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">Kafka面经</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/2022/12/10/zookeeper%E9%9D%A2%E7%BB%8F/" title="Zookeeper面经"><img class="cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Zookeeper/Zookeeper.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-12-10</div><div class="title">Zookeeper面经</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content is-expand"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#Zookeeper-%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86"><span class="toc-text">Zookeeper 基础知识</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-Zookeeper-%E5%85%A5%E9%97%A8"><span class="toc-text">1.Zookeeper 入门</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-Zookeeper-%E5%AE%89%E8%A3%85"><span class="toc-text">2.Zookeeper 安装</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-Zookeeper-%E9%9B%86%E7%BE%A4"><span class="toc-text">3.Zookeeper 集群</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%8A%A8%E6%80%81%E4%B8%8A%E4%B8%8B%E7%BA%BF%E7%9B%91%E5%90%AC%E6%A1%88%E4%BE%8B"><span class="toc-text">4.服务器动态上下线监听案例</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-Zookeeper-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E6%A1%88%E4%BE%8B"><span class="toc-text">5.Zookeeper 分布式锁案例</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-%E4%BC%81%E4%B8%9A%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98"><span class="toc-text">6.企业面试真题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#7-%E6%BA%90%E7%A0%81%E5%85%A5%E9%97%A8%E7%AE%97%E6%B3%95%E5%9F%BA%E7%A1%80"><span class="toc-text">7.源码入门算法基础</span></a></li></ol></li></ol></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2022 - 2023 By zcm</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"></div><script src="/js/categories.js?v1"></script><script src="/js/navigation.js?v1"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>